<?php
namespace App\Common\Lib\Fun\Array_;

use App\Common as com;
use App\Common\Lib\Fun\Array_\SortArray as thisSortArray;

class SortArray extends com\Lib\LibBase {

	public static int $varSorType_Asc = 0;

	public static int $varSorType_Desc = 1;

	/**
	 * 排序类型
	 * @var int [0]asc [1]desc
	 */
	private int $sorType = 0;

	private ?string $thanKey = null;

	private array $arrays = [];

	public function __construct( array $datas = [], ?string $thanKey = null, string $sort = 'asc' ) {
		$this->sorType = match( $sort ){
			'asc' => 0,
			'desc' => 1,
		};
		//
		$this->thanKey = $thanKey;
		//
		$this->covers( $datas );
		//
		parent::__construct();
	}

	/**
	 * @return string|null
	 */
	public function getThanKey(  ):?string {
		return $this->thanKey;
	}

	/**
	 * @return int
	 */
	public function count(  ):int {
		return count( $this->arrays );
	}

	/**
	 * @param int|null $site
	 * @return array|null [null]不存在
	 */
	public function getArray( ?int $site = null ):?array {
		if( $site === null ){
			return $this->arrays;
		}else{
			$data = array_slice( $this->arrays, $site, 1 )[0] ?? null;
			if( $data === null ){
				return null;
			}
			//
			return $data;
		}

	}

	/**
	 * @param int|null $site
	 * @return array
	 */
	public function &getArrayQuote( ?int $site = null ):array {
		if( $site === null ){
			return $this->arrays;
		}else{
			$arrayKey = array_keys( array_slice( $this->arrays, $site, 1, true ) )[0] ?? null;
			if( $arrayKey === null ){
				throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$sysDataNotExist );
			}
			return $this->arrays[ $arrayKey ];
		}

	}

	/**
	 * 覆盖(大量数据建议使用该方法，速度比pushs快)
	 * @param array $datas
	 * @return $this
	 */
	public function covers( array $datas ):self {
		$this->arrays = $datas;
		// 完整排序
		$this->sort( $this->arrays );
		//
		return $this;
	}

	/**
	 * @param array $datas
	 * @return void
	 */
	private function sort( array &$datas ):void {
		if( $this->sorType === self::$varSorType_Asc ){
			uasort( $datas, function ( $aValue, $bValue ){
				if( $this->thanKey === null ){
					return $aValue <=> $bValue;
				}else{
					return $aValue[ $this->thanKey ] <=> $bValue[ $this->thanKey ] ;
				}
			} );
		}else{
			uasort( $datas, function ( $aValue, $bValue ){
				if( $this->thanKey === null ){
					return $bValue <=> $aValue;
				}else{
					return $bValue[ $this->thanKey ] <=> $aValue[ $this->thanKey ];
				}
			} );
		}
	}

	/**
	 * @param array $datas
	 * @return $this
	 */
	public function pushs( array $datas ):self {
		// 如果没有数据则走covers，速度更快
		if( empty( $this->arrays ) ){
			$this->covers( $datas );
			return $this;
		}
		//
		if( $this->sorType === self::$varSorType_Asc ){
			$this->findLessThanSpliceAsAsc( $this->arrays, $datas, $this->thanKey );
		}else{
			$this->findLessThanSpliceAsDesc( $this->arrays, $datas, $this->thanKey );
		}
		//
		return $this;
	}

	/**
	 * @param mixed $data
	 * @return $this
	 */
	public function push( $data ):self {
		$this->pushs( [
			$data
		] );
		//
		return $this;
	}

	/**
	 * 去除并删除最后一个元素
	 * @return mixed
	 */
	public function pop() {
		return array_pop( $this->arrays );
	}


	/**
	 * 移除数据并返回移除的数据
	 * @param int $startSize 开始位置。[0][默认]
	 * @param int $length 删除长度位置。[1][默认]
	 * @return array
	 */
	public function rm( int $startSize = 0, int $length = 1 ):array {
		return array_splice( $this->arrays, $startSize, $length );
	}

	/**
	 * 获取第一个元素
	 * @return mixed|null [null]不存在
	 */
	public function getAsHead(  ) {
		if( empty( $this->arrays ) ){
			return null;
		}
		//
		return array_slice( $this->arrays, 0, 1 )[0];
	}

	/**
	 * 获取并删除第一个元素
	 * @return mixed|null [null]不存在
	 */
	public function popAsHead(  ) {
		if( empty( $this->arrays ) ){
			return null;
		}
		//
		return array_splice( $this->arrays, 0, 1 )[0];
	}

	/**
	 * @param $data
	 * @return mixed
	 */
	public function getArrayValue( $data ) {
		//
		if( $this->thanKey === null ){
			return $data;
		}else{
			return $data[ $this->thanKey ];
		}
	}

	/**
	 * 是否存在指定值
	 * @param $value
	 * @return bool
	 */
	public function isExistValue( $value ):bool {
		return $this->isExistValueReSiteOrNull( $value ) !== 0;
	}


	/**
	 * 是否存在指定值(存在则返回位置)
	 * @param $value
	 * @return int|null
	 */
	public function isExistValueReSiteOrNull( $value ):?int {
		$site = $this->findLessThanSite( $value );
		//
		$data = $this->getArray( $site );
		if( $data === null ){
			return null;
		}
		//
		if( $this->getArrayValue( $data ) === $value ){
			return $site;
		}else{
			return null;
		}
	}

	/**
	 * 取接近的数组
	 * @param $value
	 * @return array|null
	 */
	public function getNearArrayOrNull( $value ):?array {
		$site = $this->findLessThanSite( $value );
		if( $site === null ){
			return null;
		}
		// 第一个位置直接返回
		if( $site === 0 ){
			return $this->getArrayQuote( $site );
		}
		// 超出位置取最后元素
		if( $site >= $this->count() ){
			return $this->getArrayQuote( $this->count() - 1 );
		}
		// 取最接近的值
		$arrayA = $this->getArrayQuote( $site - 1 );
		$arrayB = $this->getArrayQuote( $site );
		$diffA = $this->getArrayValue( $arrayA ) - $value;
		$diffB = $this->getArrayValue( $arrayB ) - $value;
		//
		return abs( $diffA ) < abs( $diffB ) ? $arrayA : $arrayB;
	}

	/**
	 * 取两边接近的数组
	 * @param $value
	 * @return array|null 如果完全匹配或位于首尾的数组则返回一个数组
	 */
	public function getNearArrayAsBothOrNull( $value ):?array {
		$site = $this->findLessThanSite( $value );
		if( $site === null ){
			return null;
		}
		// 第一个位置直接返回
		if( $site === 0 ){
			$data = $this->getArray( $site );
			if( $data === null ){
				return null;
			}
			return [
				0 => $data,
				1 => null,
			];
		}
		// 超出位置取最后元素
		if( $site >= $this->count() ){
			return [
				0 => null,
				1 => $this->getArray( $this->count() - 1 ),
			];
		}
		// 取最接近的数组
		return [
			0 => $this->getArray( $site - 1 ),
			1 => $this->getArray( $site ),
		];
	}

	/**
	 * 查找并插入数据
	 * @param array $array
	 * @param $values
	 * @param string|int|null $thanKey
	 */
	private function findLessThanSpliceAsAsc( array &$array, $values, string|int|null $thanKey = null ):void {
		// 提前排序
		$this->sort( $values );
		//
		$site = $this->findLessThanSiteAsAsc( $array, $this->getArrayValue( reset( $values ) ), $thanKey );
		$mergeSplice = [];
		foreach( $values as $value ){
			// 如果断层则添加
			if(
				! empty( $mergeSplice )
				&& ( $site + 1 ) <= count( $array ) // 插入位置不能为末尾
				&& $this->getArrayValue( $value ) > $this->getArrayValue( array_slice( $array, $site, 1 )[0] )
			){
				array_splice( $array, $site, 0, $mergeSplice );
				$mergeSplice = [];
				$site = $this->findLessThanSiteAsAsc( $array, $this->getArrayValue( $value ), $thanKey, $site );
			}
			// 加入队列
			$mergeSplice[] = $value;
		}
		//
		if( ! empty( $mergeSplice ) ){
			array_splice( $array, $site, 0, $mergeSplice );
		}
	}

	/**
	 * 查找并插入数据
	 * @param array $array
	 * @param $values
	 * @param string|int|null $thanKey
	 */
	private function findLessThanSpliceAsDesc( array &$array, $values, string|int|null $thanKey = null ):void {
		// 提前排序
		$this->sort( $values );
		//
		$site = $this->findLessThanSiteAsDesc( $array, $this->getArrayValue( reset( $values ) ), $thanKey );
		$mergeSplice = [];
		foreach( $values as $value ){
			// 如果断层则添加
			if(
				! empty( $mergeSplice )
				&& ( $site + 1 ) <= count( $array ) // 插入位置不能为末尾
				&& $this->getArrayValue( $value ) < $this->getArrayValue( array_slice( $array, $site, 1 )[0] )
			){
				array_splice( $array, $site, 0, $mergeSplice );
				$mergeSplice = [];
				$site = $this->findLessThanSiteAsDesc( $array, $this->getArrayValue( $value ), $thanKey, $site );
			}
			// 加入队列
			$mergeSplice[ $this->getArrayValue( $value ) ] = $value;
		}
		//
		if( ! empty( $mergeSplice ) ){
			array_splice( $array, $site, 0, $mergeSplice );
		}
	}


	/**
	 * @param $value
	 * @param int $siteMode 位置模式。[0][默认]开头 [1]末尾
	 * @return int
	 */
	private function findLessThanSite( $value, int $siteMode = 0 ):int {
		if( $this->sorType === self::$varSorType_Asc ){
			return $this->findLessThanSiteAsAsc( $this->arrays, $value, $this->thanKey, $siteMode );
		}else{
			return $this->findLessThanSiteAsDesc( $this->arrays, $value, $this->thanKey, $siteMode );
		}
	}


	/**
	 * 二分法查找数据中间位置，比全部数据都小则为0，比全部数据多大则为count($array)
	 * @param array $array
	 * @param $value
	 * @param string|int|null $thanKey [null]不使用子键名
	 * @param int $siteMode 位置模式。[0][默认]开头 [1]末尾
	 * @param int|null $_startSite
	 * @param int|null $_endSite
	 * @return int 返回找到的位置 注：0或比count($array)大的数则可能为不存在
	 */
	private function findLessThanSiteAsAsc( array $array, $value, string|int|null $thanKey = null, int $siteMode = 0, int $_startSite = null, int $_endSite = null ):int {
		if( count( $array ) === 0 ){
			return 0;
		}
		// 取中间值
		if( $_startSite === null ){
			$site = floor( ( count( $array ) - 1 ) / 2 );
		}else{
			if( ! isset( $_endSite ) ){
				$_endSite = count( $array ) - 1;
			}
			//
			$site = $_startSite + floor( ( $_endSite - $_startSite ) / 2 );
			if( $site < 0 ){
				$site = 0;
			}
		}
		//
		$sliceDatas = array_slice( $array, $site, 1 );
		$thanValue = $this->getArrayValue( $sliceDatas[0] );
		//
		if( $_startSite !== null && $_endSite - $_startSite <= 0 ){
			if( $value > $thanValue ){
				return $site + 1;
			}else if( $value === $thanValue ){
				if( $siteMode === 1 ){
					return $site + 1;
				}else{
					return $site;
				}
			}else{
				return $site;
			}
		}
		//
		if( $value === $thanValue ){
			if( $siteMode === 1 ){
				return $this->findLessThanSiteAsAsc( $array, $value, $thanKey, $siteMode, $site + 1, $_endSite ?? count( $array ) - 1 );
			}else{
				return $this->findLessThanSiteAsAsc( $array, $value, $thanKey, $siteMode, $_startSite ?? 0, $site - 1 );
			}
		}else if( $value > $thanValue ){
			return $this->findLessThanSiteAsAsc( $array, $value, $thanKey, $siteMode, $site + 1, $_endSite ?? count( $array ) - 1 );
		}else{
			return $this->findLessThanSiteAsAsc( $array, $value, $thanKey, $siteMode, $_startSite ?? 0, $site - 1 );
		}
	}


	/**
	 * 二分法查找数据中间位置，比全部数据都小则为0，比全部数据多大则为count($array)
	 * @param array $array
	 * @param $value
	 * @param string|int|null $thanKey [null]不使用子键名
	 * @param int $siteMode 位置模式。[0][默认]开头 [1]末尾
	 * @param int|null $_startSite
	 * @param int|null $_endSite
	 * @return int 返回找到的位置 注：0或比count($array)大的数则可能为不存在
	 */
	private function findLessThanSiteAsDesc( array $array, $value, string|int|null $thanKey = null, int $siteMode = 0, int $_startSite = null, int $_endSite = null ):int {
		if( count( $array ) === 0 ){
			return 0;
		}
		// 取中间值
		if( $_startSite === null ){
			$site = floor( ( count( $array ) - 1 ) / 2 );
		}else{
			if( ! isset( $_endSite ) ){
				$_endSite = count( $array ) - 1;
			}
			//
			$site = $_startSite + floor( ( $_endSite - $_startSite ) / 2 );
			if( $site < 0 ){
				$site = 0;
			}
		}
		//
		$sliceDatas = array_slice( $array, $site, 1 );
		$thanValue = $thanKey !== null ? $sliceDatas[0][ $thanKey ] : $sliceDatas[0];
		//
		if( $_startSite !== null && $_endSite - $_startSite <= 0 ){
			if( $value < $thanValue ){
				return $site + 1;
			}else if( $value === $thanValue ){
				if( $siteMode === 1 ){
					return $site + 1;
				}else{
					return $site;
				}
			}else{
				return $site;
			}
		}
		//
		if( $value === $thanValue ){
			if( $siteMode === 1 ){
				return $this->findLessThanSiteAsDesc( $array, $value, $thanKey, $siteMode, $site + 1, $_endSite ?? count( $array ) - 1 );
			}else{
				return $this->findLessThanSiteAsDesc( $array, $value, $thanKey, $siteMode, $_startSite ?? 0, $site - 1 );
			}

		}else if( $value < $thanValue ){
			return $this->findLessThanSiteAsDesc( $array, $value, $thanKey, $siteMode, $site + 1, $_endSite ?? count( $array ) - 1 );
		}else{
			return $this->findLessThanSiteAsDesc( $array, $value, $thanKey, $siteMode, $_startSite ?? 0, $site - 1 );
		}
	}

	// ===============================
	//
	//
	// 下面为静态方法
	//
	//
	// ===============================

	/**
	 * @param array $datas
	 * @param string|null $thanKey
	 * @param string $sort
	 * @return $this
	 */
	public static function makeOfNew( array $datas = [], ?string $thanKey = null, string $sort = 'asc' ):self {
	    return self::make( $datas, $thanKey, $sort );
	}

}
